﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using FlexminAPI;
using System.IO;
using System.Threading;
using System.Xml;
using System.Net;
using FlexLib;
using FlexLib.Plugins;
using FlexminAdmin.Tree;
using Aga.Controls.Tree;
using System.Diagnostics;

namespace FlexminAdmin.Forms
{
	public delegate void SelectChangeDelegate(object sender, EventArgs e);

	public partial class MainForm : Form
	{
		private class PluginDownloadInfo
		{
			private IHost _host;
			public IHost Host
			{
				get { return _host; }
			}

			private string _pluginName;
			public string PluginName
			{
				get { return _pluginName; }
			}

			public PluginDownloadInfo(IHost host, string pluginName)
			{
				_host = host;
				_pluginName = pluginName;
			}
		}

		private List<Bitmap> computerStateIcons;
		private IEnumerable<Plugin> plugins;
		private string listFilePath = null;
		private PluginCopyForm pluginCopyForm;
		private FlexminTreeModel treeModel = new FlexminTreeModel();

		protected void InitPlugins()
		{
			plugins = PluginManager.AdminInstance.Plugins;
			PluginManager.AdminInstance.Change += new EventHandler(AdminInstance_Change);
		}

		void AdminInstance_Change(object sender, EventArgs e)
		{
			plugins = PluginManager.AdminInstance.Plugins;
		}

		protected void InitIcons()
		{
			computerStateIcons = new List<Bitmap>();
			computerStateIcons.Add(Properties.Resources.ComputerOff);
			computerStateIcons.Add(Properties.Resources.ComputerOn);
            computerStateIcons.Add(Properties.Resources.ComputerNotAutentificated);
			computerStateIcons.Add(Properties.Resources.ComputerReady);
		}

		public MainForm()
		{
			InitializeComponent();

			hostTree.Model = treeModel;
			InitPlugins();
			InitIcons();

			treeModel.Clear();
			XmlTreeSerializer.Deserialize(Path.GetDirectoryName(Application.ExecutablePath) +
						Path.DirectorySeparatorChar +
						"Default.fmh",
						treeModel,
						hostTree);

			SetEventHandler(treeModel.Root);
		}

		private void SetEventHandler(Node node)
		{
			if (node is ComputerTreeNode)
			{
				ComputerTreeNode computerNode = node as ComputerTreeNode;
				if (computerNode.Host != null)
					computerNode.Host.StateChanged += new HostUpdateDelegate(remoteComputer_StateChanged);
			}

			foreach (Node subNode in node.Nodes)
			{
				SetEventHandler(subNode);
			}
		}

		private void HostTree_AfterSelect(object sender, EventArgs e)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new EventHandler(HostTree_AfterSelect), new object[] { sender, e });
				return;
			}

			UpdatePluginsView();
		}

		private void UpdatePluginsView()
		{
			//			HostsTabs.TabPages.Add(HostBox.Items[HostBox.SelectedIndex].ToString());
			UtilView.Items.Clear();
			smallImageList.Images.Clear();

			if (hostTree.SelectedNode == null || !(hostTree.SelectedNode.Tag is ComputerTreeNode))
			{
				UtilView.Enabled = false;
				return;
			}

			IHost host = (hostTree.SelectedNode.Tag as ComputerTreeNode).Host;
			if (host == null)
			{
				UtilView.Enabled = false;
				return;
			}

			Monitor.Enter(host);
			ComputerState computerState = host.State;
			List<PluginInfo> remotePlugins = new List<PluginInfo>(host.GetPlugins());
			Monitor.Exit(host);

			passwordBox.Text = "";
			passwordBox.Enabled =
				computerState == ComputerState.Ready ||
				computerState == ComputerState.NotAutentificated;

			if (computerState != ComputerState.Ready)
			{
				UtilView.Enabled = false;
				return;
			}

			UtilView.Enabled = true;

			//Добавление в список плагинов, присутствующих на удаленном компе
			foreach (PluginInfo pluginInfo in remotePlugins)
			{
				ListViewItem item = UtilView.Items.Add(pluginInfo.DisplayName);

				//Поиск такого плагина в плагинах на этом компе
				Plugin foundPlugin = null;
				foreach (Plugin plugin in plugins)
				{
					if (plugin.Info.Name.Equals(pluginInfo.Name))
					{
						foundPlugin = plugin;
						break;
					}
				}
				//Устанавливаем нужное описание и цвет плагину исходя из состояния
				if (foundPlugin == null)
				{
					item.ForeColor = Color.Red;
					item.SubItems.Add("Missing");
					item.Tag = new PluginListInfo(pluginInfo, PluginDifference.MissingAdminSide);
				}
				else
				{
					int iImageIndex = UtilView.SmallImageList.Images.Count;
					smallImageList.Images.Add(foundPlugin.Master.GetIcon());
					item.ImageIndex = iImageIndex;

					if (foundPlugin.Info.PluginVersion == pluginInfo.PluginVersion)
					{
						item.ForeColor = Color.Black;
						item.SubItems.Add("Installed");
						item.Tag = new PluginListInfo(pluginInfo, PluginDifference.FullMatch);
					}
					else
					{
						item.ForeColor = Color.Brown;
						item.SubItems.Add("Version mismatch");
						item.SubItems.Add(foundPlugin.Info.PluginVersion.ToString() + " (" + pluginInfo.PluginVersion.ToString() + ")");
						item.Tag = new PluginListInfo(pluginInfo, PluginDifference.VersionMismatch);
						continue;
					}
				}
				item.SubItems.Add(pluginInfo.PluginVersion.ToString());
			}

			//Добавление в список плагинов те, которые присутствуют на этом компе, но которых нет на удаленном
			foreach (Plugin plugin in plugins)
			{
				PluginInfo info = null;
				foreach (PluginInfo information in remotePlugins)
				{
					if (plugin.Info.Name.Equals(information.Name))
					{
						info = information;
						break;
					}
				}

				if (info == null)
				{
					ListViewItem item = UtilView.Items.Add(plugin.Info.DisplayName);
					item.Tag = plugin.Info;
					int iImageIndex = UtilView.SmallImageList.Images.Count;
					UtilView.SmallImageList.Images.Add(plugin.Master.GetIcon());
					item.ImageIndex = iImageIndex;

					item.ForeColor = Color.Gray;
					item.SubItems.Add("Not installed");
					item.SubItems.Add(plugin.Info.PluginVersion.ToString());
					item.Tag = new PluginListInfo(plugin.Info, PluginDifference.MissingHostSide);
				}
			}
		}

		private static ISlave GetRemoteObject(string host, string objName)
		{
			return Activator.GetObject(typeof(ISlave), "tcp://" + host + ":" + NetHelp.SERVER_REMOTING_PORT.ToString() + "/" + objName) as ISlave;
		}

		private const int BLOCK_SIZE = 20480;

		private void DownloadPluginThread(object _pluginInfo)
		{
			PluginDownloadInfo pluginInfo = _pluginInfo as PluginDownloadInfo;
			IPHostEntry hostEntry = null;

			bool error = false;
			try
			{
				hostEntry = Dns.GetHostEntry(pluginInfo.Host.HostAddress);
			}
			catch
			{
				error = true;
			}

			if (error || (hostEntry.AddressList.Length < 1))
			{
				pluginCopyForm.DoClose();
				return;
			}

			IPAddress address = null;
			foreach (IPAddress addr in hostEntry.AddressList)
			{
				if (addr.AddressFamily == AddressFamily.InterNetwork)
				{
					address = addr;
					break;
				}
			}

			if (address == null)
			{
				pluginCopyForm.DoClose();
				return;
			}

			SocketReader reader = null;
			SocketWriter writer = null;
			try
			{
				IPEndPoint ipe = new IPEndPoint(address, NetHelp.SERVER_TCP_PORT);
				Socket tempSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

				tempSocket.Connect(ipe);

				if (tempSocket.Connected)
				{
					reader = new SocketReader(tempSocket);
					writer = new SocketWriter(tempSocket);
				}

				if (reader == null)
				{
					pluginCopyForm.DoClose();
					return;
				}
			}
			catch (SocketException)
			{
				pluginCopyForm.DoClose();
				return;
			}

			int pluginSize;
			try
			{
				writer.WriteString(pluginInfo.Host.Password);
				int answer = reader.ReadInt();
				if (answer != NetHelp.PASSWORD_OK)
				{
					reader.Close();
					pluginCopyForm.DoClose();
					return;
				}

				writer.WriteInt(NetHelp.ACTION_DOWNLOAD_PLUGIN);
				writer.WriteString(pluginInfo.PluginName);
				pluginSize = reader.ReadInt();
			}
			catch
			{
				writer.Close();
				pluginCopyForm.DoClose();
				return;
			}

			string packagePath = Path.GetTempFileName();

			BinaryWriter fileWriter = new BinaryWriter(new FileStream(packagePath, FileMode.Create, FileAccess.Write));
			pluginCopyForm.SetFileLength(pluginSize);

			try
			{
				while (pluginSize > 0)
				{
					int copySize = pluginSize > BLOCK_SIZE ? BLOCK_SIZE : pluginSize;
					pluginSize -= copySize;

					byte[] data = reader.ReadBytes(copySize);
					fileWriter.Write(data);
					pluginCopyForm.Advance(copySize);
				}
			}
			catch (ThreadAbortException)
			{
				fileWriter.Close();
				reader.Close();
				return;
			}
			catch
			{
				fileWriter.Close();
				reader.Close();
				pluginCopyForm.DoClose();
				return;
			}
			
			fileWriter.Close();

			reader.Close();

			try
			{
				PluginManager.AdminInstance.Install(packagePath);
			}
			catch (PluginException ex)
			{
				pluginCopyForm.DoClose();
				// TODO: нельзя показывать окно из другого (не GUI) потока
				MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

			pluginCopyForm.DoClose();
			Thread.Sleep(1000);
			HostTree_AfterSelect(this, new EventArgs());
		}

		private void UploadPluginTread(object _pluginInfo)
		{
			PluginDownloadInfo pluginInfo = _pluginInfo as PluginDownloadInfo;
			IPHostEntry hostEntry = null;

			bool error = false;
			try
			{
				hostEntry = Dns.GetHostEntry(pluginInfo.Host.HostAddress);
			}
			catch
			{
				error = true;
			}

			if (error || (hostEntry.AddressList.Length < 1))
			{
				pluginCopyForm.DoClose();
				return;
			}

			IPAddress address = null;
			foreach (IPAddress addr in hostEntry.AddressList)
			{
				if (addr.AddressFamily == AddressFamily.InterNetwork)
				{
					address = addr;
					break;
				}
			}

			if (address == null)
			{
				pluginCopyForm.DoClose();
				return;
			}

			SocketReader reader = null;
			SocketWriter writer = null;
			try
			{
				IPEndPoint ipe = new IPEndPoint(address, NetHelp.SERVER_TCP_PORT);
				Socket tempSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

				tempSocket.Connect(ipe);

				if (tempSocket.Connected)
				{
					reader = new SocketReader(tempSocket);
					writer = new SocketWriter(tempSocket);
				}

				if (reader == null)
				{
					pluginCopyForm.DoClose();
					return;
				}
			}
			catch (SocketException)
			{
				pluginCopyForm.DoClose();
				return;
			}

			try
			{
				writer.WriteString(pluginInfo.Host.Password);
				int answer = reader.ReadInt();
				if (answer != NetHelp.PASSWORD_OK)
				{
					reader.Close();
					pluginCopyForm.DoClose();
					return;
				}

				writer.WriteInt(NetHelp.ACTION_UPLOAD_PLUGIN);
			}
			catch
			{
				writer.Close();
				pluginCopyForm.DoClose();
				return;
			}

			string packagePath = PluginManager.HostInstance.GetModulePath(pluginInfo.PluginName);
			packagePath += Path.DirectorySeparatorChar + pluginInfo.PluginName + ".fpkg";
			BinaryReader fileReader = new BinaryReader(new FileStream(packagePath, FileMode.Open, FileAccess.Read));

			int fileSize = (int)fileReader.BaseStream.Length;
			pluginCopyForm.SetFileLength(fileSize);

			try
			{
				writer.WriteInt(fileSize);

				while (fileSize > 0)
				{
					int copySize = fileSize > BLOCK_SIZE ? BLOCK_SIZE : fileSize;
					fileSize -= copySize;

					byte[] data = fileReader.ReadBytes(copySize);
					writer.WriteBytes(data);
					pluginCopyForm.Advance(copySize);
				}
			}
			catch (ThreadAbortException)
			{
				fileReader.Close();
				reader.Close();
				return;
			}
			catch
			{
				fileReader.Close();
				reader.Close();
				pluginCopyForm.DoClose();
				return;
			}

			fileReader.Close();

			pluginCopyForm.DoClose();
			reader.ReadInt();
			reader.Close();
			Thread.Sleep(1000);
			HostTree_AfterSelect(this, new EventArgs());
		}

		private void UtilView_DoubleClick(object sender, EventArgs e)
		{
			if (hostTree.SelectedNode == null)
				return;

			ComputerTreeNode selectedNode = hostTree.SelectedNode.Tag as ComputerTreeNode;
			if (selectedNode == null)
				return;

			IHost host = selectedNode.Host;

			int selectedIndex;
			if (UtilView.SelectedIndices.Count < 1)
				return;
			else
				selectedIndex = UtilView.SelectedIndices[0];

			PluginListInfo pluginListInfo = UtilView.Items[selectedIndex].Tag as PluginListInfo;
			if (pluginListInfo == null)
				return;

			switch (pluginListInfo.Difference)
			{
				case PluginDifference.FullMatch:
					foreach (Plugin plugin in plugins)
					{
						if (plugin.Info.Equals(pluginListInfo.Info))
						{
							ISlave slave = GetRemoteObject(host.HostAddress, pluginListInfo.Info.RemotingName);
							plugin.Master.Initialize(slave, host);
							Form form = plugin.Master.GetUIForm();
							try
							{
								form.ShowDialog();
							}
							catch (ObjectDisposedException)	{	}
							break;
						}
					}
					break;

				case PluginDifference.MissingHostSide:
					if (MessageBox.Show("Do you want to upload this module to remote computer?", "Module missing", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
					{
						UploadPlugin(host, pluginListInfo.Info.Name);
					}
					break;

				case PluginDifference.MissingAdminSide:
					if (MessageBox.Show("Do you want to download this module from the remote computer?", "Module missing", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
					{
						DownloadPlugin(host, pluginListInfo.Info.Name);
					}
					break;

				case PluginDifference.VersionMismatch:
					ICollection<PluginInfo> pluginsInfo = host.GetPlugins();
					PluginInfo remotePluginInfo = null;
					foreach (PluginInfo pluginInfo in pluginsInfo)
					{
						if (pluginListInfo.Info.Name == pluginInfo.Name)
						{
							remotePluginInfo = pluginInfo;
							break;
						}
					}

					if (remotePluginInfo == null)
						return;

					VersionMismatchForm versionMismatchForm =
						new VersionMismatchForm(pluginListInfo.Info.PluginVersion, remotePluginInfo.PluginVersion);
					DialogResult result = versionMismatchForm.ShowDialog();

					if (result == DialogResult.Yes)
						DownloadPluginNewVersion(host, pluginListInfo.Info.Name);
					else if (result == DialogResult.No)
						UploadPlugin(host, pluginListInfo.Info.Name);
					break;
			}
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			XmlTreeSerializer.Serialize(Path.GetDirectoryName(Application.ExecutablePath) +
					Path.DirectorySeparatorChar +
					"Default.fmh",
					treeModel, hostTree);

			treeModel.Clear();			
		}

		private void AddHostToTree(IHost host)
		{
			if (hostTree.SelectedNode == null)
			{
				if (hostTree.Root.Children.Count == 0)
					return;

				hostTree.SelectedNode = hostTree.Root.Children[0];
			}

			RemoteComputer remoteComputer = host as RemoteComputer;
			remoteComputer.StateChanged += new HostUpdateDelegate(remoteComputer_StateChanged);

			ComputerTreeNode newNode = new ComputerTreeNode(remoteComputer);

			TreeNodeAdv tnode = hostTree.SelectedNode;
			if (!(tnode.Tag is GroupTreeNode))
				tnode = tnode.Parent;

			(tnode.Tag as GroupTreeNode).Nodes.Add(newNode);
			tnode.IsExpanded = true;
		}

		void remoteComputer_StateChanged(IHost sender, HostEventArgs e)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new HostUpdateDelegate(remoteComputer_StateChanged), new object[] { sender, e });
				return;
			}

			if (hostTree.SelectedNode == null)
				return;

			if (!(hostTree.SelectedNode.Tag is ComputerTreeNode))
				return;

			ComputerTreeNode node = hostTree.SelectedNode.Tag as ComputerTreeNode;

			if (node.Host != sender)
				return;

			UpdatePluginsView();
		}

		private void AddHostToTree(string hostAddress, string pasword)
		{
			RemoteComputer remoteComputer = new RemoteComputer(hostAddress, pasword);
			AddHostToTree(remoteComputer);
		}

		private void AddToolStripMenuItem_Click(object sender, EventArgs e)
		{
			AddHostForm form = new AddHostForm();
			if (form.ShowDialog() == DialogResult.OK)
			{
				AddHostToTree(form.HostAddress, "");
			}
		}

		private void deleteFromTree_Click(object sender, EventArgs e)
		{
			if (hostTree.SelectedNode == null)
				return;

			if (hostTree.SelectedNode.Tag is GroupTreeNode)
			{
				Node node = hostTree.SelectedNode.Tag as Node;

				if (node.Parent == treeModel.Root)
					return;

				if (MessageBox.Show("Delete group?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
					return;

				treeModel.CloseConnectionsInNode(node);
				node.Parent = null;
				return;
			}

			if (!(hostTree.SelectedNode.Tag is ComputerTreeNode))
				return;

			if (MessageBox.Show("Do you really want to delete this computer?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
				return;


			ComputerTreeNode computerNode = hostTree.SelectedNode.Tag as ComputerTreeNode;
			IHost computer = computerNode.Host;
			if (computer != null)
			{
				computer.StateChanged -= remoteComputer_StateChanged;
				computer.ShutDown();
				computerNode.Parent = null;
			}
		}

		private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
		{
			Close();
		}

		private void newListToolStripMenuItem_Click(object sender, EventArgs e)
		{
			treeModel.Clear();
		}

		private void saveHostListToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (listFilePath == null)
			{
				saveHostListAsToolStripMenuItem_Click(sender, e);
				return;
			}

			XmlTreeSerializer.Serialize(listFilePath, treeModel, hostTree);
		}

		private void saveHostListAsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (saveFileDialog.ShowDialog() != DialogResult.OK)
				return;

			listFilePath = saveFileDialog.FileName;

			XmlTreeSerializer.Serialize(saveFileDialog.FileName, treeModel, hostTree);
		}

		private void openListToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (openFileDialog.ShowDialog() != DialogResult.OK)
				return;

			listFilePath = openFileDialog.FileName;

			XmlTreeSerializer.Deserialize(openFileDialog.FileName,
				treeModel,
				hostTree);
			SetEventHandler(treeModel.Root);
		}

		private void passwordBox_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == '\r')
			{
				if (hostTree.SelectedNode == null)
					return;

				if (!(hostTree.SelectedNode.Tag is ComputerTreeNode))
					return;

				ComputerTreeNode node = hostTree.SelectedNode.Tag as ComputerTreeNode;
				IHost comp = node.Host;
				if (comp == null)
					return;

				TextBox passBox = (sender as TextBox);

				if (comp.State == ComputerState.Ready)
				{
					ChangePasswordForm form = new ChangePasswordForm(comp, passBox.Text);
					form.ShowDialog();
					return;
				}

				comp.Password = passBox.Text;
				passBox.Text = "";
			}
		}

		private void DownloadPlugin(IHost host, string pluginName)
        {
			Thread copyThread = new Thread(new ParameterizedThreadStart(DownloadPluginThread));
			pluginCopyForm = new PluginCopyForm();
			copyThread.Start(new PluginDownloadInfo(host, pluginName));
			pluginCopyForm.ShowDialog();
        }

		private void DownloadPluginNewVersion(IHost host, string pluginName)
		{
			Thread copyThread = new Thread(new ParameterizedThreadStart(DownloadPluginThread));
			pluginCopyForm = new PluginCopyForm();
			copyThread.Start(new PluginDownloadInfo(host, pluginName));
			pluginCopyForm.ShowDialog();
		}

		private void UploadPlugin(IHost host, string pluginName)
		{
			Thread copyThread = new Thread(new ParameterizedThreadStart(UploadPluginTread));
			pluginCopyForm = new PluginCopyForm();
			copyThread.Start(new PluginDownloadInfo(host, pluginName));
			pluginCopyForm.ShowDialog();
		}

		private void FindStripButton_Click(object sender, EventArgs e)
		{
			ComputerSearchForm searchForm = new ComputerSearchForm();
			searchForm.AddHost += new AddComputerDelegate(searchForm_AddComputer);
			searchForm.ShowDialog();
		}

		void searchForm_AddComputer(IHost host)
		{
			AddHostToTree(host);
		}

		private void RefreshNode(Node node)
		{
			if (node is ComputerTreeNode)
			{
				IHost host = (node as ComputerTreeNode).Host;
				if (host != null)
					host.Refresh();
			}
			
			foreach (Node subNode in node.Nodes)
				RefreshNode(subNode);		
		}

		private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
		{
			foreach (Node node in treeModel.Nodes)
				RefreshNode(node);
		}

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			AboutForm form = new AboutForm();
			form.ShowDialog();
		}

		private void pluginsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			PluginsForm pluginsForm;
			pluginsForm = new PluginsForm();
			pluginsForm.Show();
		}

		private void deleteToolStripMenuItem1_Click(object sender, EventArgs e)
		{
			if (UtilView.SelectedItems.Count != 1)
				return;

			PluginListInfo pluginListInfo = UtilView.SelectedItems[0].Tag as PluginListInfo;
			if (pluginListInfo == null)
				return;

			if (pluginListInfo.Difference == PluginDifference.MissingHostSide)
				return;

		}

		private void UtilView_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				ListViewItem item = UtilView.GetItemAt(e.X, e.Y);
				if (item == null)
					return;
				UtilView.SelectedItems.Clear();
				item.Selected = true;
			}
		}

		private void AddGroup()
		{
			AddGroup("New Group");
		}

		private void AddGroup(string name)
		{
			if (hostTree.SelectedNode == null)
			{
				if (hostTree.Root.Children.Count == 0)
					return;

				hostTree.SelectedNode = hostTree.Root.Children[0];
			}
			
			if (hostTree.SelectedNode.Tag is GroupTreeNode)
			{
				(hostTree.SelectedNode.Tag as GroupTreeNode).Nodes.Add(new GroupTreeNode(name));
				hostTree.SelectedNode.IsExpanded = true;
			}
			else
			{
				if (hostTree.SelectedNode.Tag is ComputerTreeNode)
				{
					hostTree.SelectedNode = hostTree.SelectedNode.Parent;
					AddGroup(name);
				}
			}
		}

		private void addGroupToolStripMenuItem_Click(object sender, EventArgs e)
		{
			AddGroup();
		}

		private void hostTree_DragDrop(object sender, DragEventArgs e)
		{
			TreeViewAdv tree = sender as TreeViewAdv;

			TreeNodeAdv[] nodes = (TreeNodeAdv[])e.Data.GetData(typeof(TreeNodeAdv[]));
			Node dropNode = tree.DropPosition.Node.Tag as Node;
			if (tree.DropPosition.Position == NodePosition.Inside
				&& dropNode is GroupTreeNode)
			{
				foreach (TreeNodeAdv n in nodes)
				{
					(n.Tag as Node).Parent = dropNode;
				}
				tree.DropPosition.Node.IsExpanded = true;
			}
			else 
				if (tree.DropPosition.Position != NodePosition.Inside
					&& dropNode.Parent is GroupTreeNode)
			{
				Node parent = dropNode.Parent;
				Node nextItem = dropNode;
				if (tree.DropPosition.Position == NodePosition.After)
					nextItem = dropNode.NextNode;

				foreach (TreeNodeAdv node in nodes)
					(node.Tag as Node).Parent = null;

				int index = -1;
				index = parent.Nodes.IndexOf(nextItem);
				foreach (TreeNodeAdv node in nodes)
				{
					Node item = node.Tag as Node;
					if (index == -1)
						parent.Nodes.Add(item);
					else
					{
						parent.Nodes.Insert(index, item);
						index++;
					}
				}
			}
		}

		private void hostTree_ItemDrag(object sender, ItemDragEventArgs e)
		{
			TreeViewAdv tree = sender as TreeViewAdv;
			tree.DoDragDropSelectedNodes(DragDropEffects.Move);
		}

		private void hostTree_DragOver(object sender, DragEventArgs e)
		{
			TreeViewAdv tree = sender as TreeViewAdv;

			if (e.Data.GetDataPresent(typeof(TreeNodeAdv[]))
				&& tree.DropPosition.Node != null)
			{
				TreeNodeAdv[] nodes = e.Data.GetData(typeof(TreeNodeAdv[])) as TreeNodeAdv[];
				TreeNodeAdv parent = tree.DropPosition.Node;
				if (tree.DropPosition.Position != NodePosition.Inside)
					parent = parent.Parent;

				foreach (TreeNodeAdv node in nodes)
					if (!CheckNodeParent(parent, node))
					{
						e.Effect = DragDropEffects.None;
						return;
					}

				e.Effect = e.AllowedEffect;
			}
		}

		private bool CheckNodeParent(TreeNodeAdv parent, TreeNodeAdv node)
		{
			while (parent != null)
			{
				if (node == parent)
					return false;
				else
					parent = parent.Parent;
			}
			return true;
		}
	}
}